class Agent(object):
    
    NAME = "CEREAL_KILLERS_VERSION_2"
    

    # =============== global variables =================
    FIRST_OBJECTIVE = None
    NEW_OBJECTIVE = 0
    GAME_STARTED = False
    AMMO_LOCATIONS = []
    UNOC = []
    # ==================================================


    
    def __init__(self, id, team, settings=None, field_rects=None, field_grid=None, nav_mesh=None, blob=None):
        """ Each agent is initialized at the beginning of each game.
            The first agent (id==0) can use this to set up global variables.
            Note that the properties pertaining to the game field might not be
            given for each game.
        """
        self.id = id
        self.team = team
        self.mesh = nav_mesh
        self.grid = field_grid
        self.settings = settings
        self.goal = None
        
        # Read the binary blob, we're not using it though
        if blob is not None:
            print "Agent %s-%d received binary blob of %s" % (
                ('BLU' if team else 'RED'), id, type(pickle.loads(blob.read())))
            # Reset the file so other agents can read it.
            blob.seek(0) 
        
        # Recommended way to share variables between agents.
        if id == 0:
            self.all_agents = self.__class__.all_agents = []
        self.all_agents.append(self)
    
  
    def observe(self, observation):
        """ Each agent is passed an observation using this function,
            before being asked for an action. You can store either
            the observation object or its properties to use them
            to determine your action. Note that the observation object
            is modified in place.
        """
        self.observation = observation
        self.selected = observation.selected
        obs = observation
        
        if observation.selected:
            pass
            # print observation
           
	    

		

##        self.step       = 0     #: Current timestep
##        self.loc        = (0,0) #: Agent's location (x,y)
##        self.angle      = 0     #: Current angle in radians
##        self.walls      = []    #: Visible walls around the agent: a 2D binary array
##        self.friends    = []    #: All/Visible friends: a list of (x,y,angle)-tuples
##        self.foes       = []    #: Visible foes: a list of (x,y,angle)-tuples
##        self.cps        = []    #: Controlpoints: a list of (x,y,TEAM_RED/TEAM_BLUE)-tuples
##        self.objects    = []    #: Visible objects: a list of (x,y,type)-tuples
##        self.ammo       = 0     #: Ammo count
##        self.score      = (0,0) #: Current game score
##        self.collided   = False #: Whether the agent has collided in the previous turn
##        self.respawn_in = -1    #: How many timesteps left before this agent can move again.
        
##        # The following properties are only set when
##        # the renderer is enabled:
##        self.selected = False   #: Indicates if the agent is selected in the UI
##        self.clicked = None     #: Indicates the position of a right-button click, if there was one
##        self.keys = []          #: A list of all keys pressed in the previous turn

       
 		 
       


# Cem's functions =========================

    """ This function should return a list of object
        tuples, each tuple containing the closest for
        certain "object", as listed below:

    """
    #closestSth[0][0:2] for cps
    #closestSth[1][0:2] for ammo
    #closestSth[2][0:2] for foe
    def get_closest(self):
     
        #friend = point_dist(obs.loc, obs.friends)
        obs = self.observation
        
        closestAmmo = []
        closestFoe = []
        closestAll = []

    
        cpDists = []                                        #cpDists holds the distances of each ctrl point to the agent
        if obs.cps != []:
            for x in obs.cps:
                cpDists.append(point_dist(x[0:2],obs.loc))
                
            
            closestCpindex = cpDists.index(min(cpDists))
            closestCp = obs.cps[closestCpindex]             #the closest Cp


        ammoDists = []                                      #ammoDists holds the distances of each ammo to the agent
        if obs.objects != []:
            for x in obs.objects:
                ammoDists.append(point_dist(x[0:2],obs.loc))

            
            closestAmmoindex = ammoDists.index(min(ammoDists))
            closestAmmo = obs.objects[closestAmmoindex]             #the closest ammo
            

        foeDists = [] 
        if obs.foes != []:
            for x in obs.foes:
                foeDists.append(point_dist(x[0:2],obs.loc))

            
            closestFoeindex = foeDists.index(min(foeDists))
            closestFoe = obs.foes[closestFoeindex]             #the closest ammo


##        allDists = []
##        for x in cpDists:
##            allDists.append(x)
##
##        for x in ammoDists:
##            allDists.append(x)
##
##        for x in foeDists:
##            allDists.append(x)

        #print "     "
        #print cpDists
        #print allDists
            

            
        #closestAllindex = allDists.index(min(allDists))
        #closestAll = obs.cps[closestAllindex]           #the closest Cp
        
            

        #return (closestCp,closestAmmo,closestFoe,closestAll)
        return (closestCp,closestAmmo,closestFoe)



    def get_closest_unoc_cp(self):
        obs = self.observation
        cps = obs.cps[:]
        #cpDists = []
        toSort = []
           
        for x in obs.cps:
            #cpDists.append(point_dist(x[0:2],obs.loc))
            toSort.append((x[0],x[1],x[2],point_dist(x[0:2],obs.loc)))

        

        sortedForDist = sorted(toSort, key=lambda closest: closest[3])

        if sortedForDist[0][2] != self.team:
            closestCp=sortedForDist[0][0:2]

        elif sortedForDist[1][2] != self.team:
            closestCp=sortedForDist[1][0:2]
            
        elif sortedForDist[2][2] != self.team:
            closestCp=sortedForDist[2][0:2]
        else:
            closestCp=-1;
        
          
        return(closestCp)
        

      



    
    
    def action(self):
        obs = self.observation
        shoot = False
        speed = 40



        Goal = self.get_closest_unoc_cp()
        if Goal != -1:
            self.goal = Goal
        
        if self.id == 3:
            if obs.cps[0][2] == self.team:
                Goal = self.get_closest_unoc_cp()
                if Goal != -1:
                    self.goal = Goal
            else:
                self.goal = obs.cps[0][0:2]

        if self.id == 4:
            if obs.cps[1][2] == self.team:
                Goal = self.get_closest_unoc_cp()
                if Goal != -1:
                    self.goal = Goal
            else:
                self.goal = obs.cps[1][0:2]
           

        if self.id == 5:
            if obs.cps[2][2] == self.team:
                Goal = self.get_closest_unoc_cp()
                if Goal != -1:
                    self.goal = Goal
            else:
                self.goal = obs.cps[2][0:2]



        # for agent 0
        if self.id == 0:


            
            
            
            ## ======= if ammo point in objects list is not present in AMMO_LOCATIONS list, then add it:

            for x in obs.objects:
                if x not in self.__class__.AMMO_LOCATIONS:
                    self.__class__.AMMO_LOCATIONS.append(x)

                    # now handle the ammo on the opposite half of screen
                    mirrorX = 656 - x[0]  # THE WIDTH OF THE SCREEN IS HARDCODED HERE. THIS SHOULD BE GENERIC!!!!
                    mirrorY = x[1]
                    mirror = (mirrorX, mirrorY, x[2])
                    if mirror not in self.__class__.AMMO_LOCATIONS:
                        self.__class__.AMMO_LOCATIONS.append(mirror)


        # ===================================
        # THE FOLLOWING APPLIES TO ALL AGENTS
        # ===================================


        # Walk to ammo
        ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
        pickingUpAmmo = False
        if ammopacks and obs.ammo < 1:     # only go for ammo if agent has less than 1 shots
            oldAmmoGoal = self.goal # remember previous goal
            self.goal = self.get_closest()[1][0:2]
            pickingUpAmmo = True

        # Shoot enemies
        shoot = False
        shootingEnemy = False
        if (obs.ammo > 0 and 
            obs.foes and 
            point_dist(obs.foes[0][0:2], obs.loc) < self.settings.max_range
            and not line_intersects_grid(obs.loc, obs.foes[0][0:2], self.grid, self.settings.tilesize)):
            oldGoal = self.goal # remember previous goal so that after shooting agent can continue with previous goal
            self.goal = obs.foes[0][0:2]
            shoot = True
            shootingEnemy = True


        


        # Compute path, angle and drive
        path = find_path(obs.loc, self.goal, self.mesh, self.grid, self.settings.tilesize)
        if path:
            dx = path[0][0]-obs.loc[0]
            dy = path[0][1]-obs.loc[1]
            turn = angle_fix(math.atan2(dy, dx)-obs.angle)
            if turn > self.settings.max_turn or turn < -self.settings.max_turn:
                shoot = False
                if shootingEnemy == True:
                    speed = 0                            # if trying to shoot enemy, only turn without moving position
                else:
                    speed = ((dx**2 + dy**2)**0.5) / 3   # use a smaller moving step if turn is too big so that agent does not understeer
            else:
                speed = (dx**2 + dy**2)**0.5
        else:
            turn = 0
            speed = 0



        if pickingUpAmmo == True:
            self.goal = oldAmmoGoal # after picking up ammo / moving towards ammo, change back to original goal

        if shoot == True:    # after shooting an agent, switch back to the goal it had before encountering enemy
            self.goal = oldGoal
        
        return (turn,speed,shoot)







        
    def debug(self, surface):
        """ Allows the agents to draw on the game UI,
            Refer to the pygame reference to see how you can
            draw on a pygame.surface. The given surface is
            not cleared automatically. Additionally, this
            function will only be called when the renderer is
            active, and it will only be called for the active team.
        """
        import pygame
        # First agent clears the screen
        if self.id == 0:
            surface.fill((0,0,0,0))
        # Selected agents draw their info
        if self.selected:
            if self.goal is not None:
                pygame.draw.line(surface,(0,0,0),self.observation.loc, self.goal)
        
    def finalize(self, interrupted=False):
        """ This function is called after the game ends, 
            either due to time/score limits, or due to an
            interrupt (CTRL+C) by the user. Use it to
            store any learned variables and write logs/reports.
        """
        pass
